package jmine.tec.test.utils.thread;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.hibernate.cfg.Environment;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.connection.DriverManagerConnectionProvider;

/**
 * A hibernate ConnectionProvider that creates one DriverManagerConnectionProvider per thread.
 * 
 * @author jrenaut
 */
public class ThreadBoundDriverManagerConnectionProvider implements ConnectionProvider {

    private static final Logger LOGGER = Logger.getLogger(ThreadBoundDriverManagerConnectionProvider.class);

    private static ThreadLocal<ConnectionProvider> localProviders = new ThreadLocal<ConnectionProvider>();

    private Properties properties;

    private List<ConnectionProvider> providers = new ArrayList<ConnectionProvider>();

    /**
     * Closes the Connection Provider
     */
    public void close() {
        for (ConnectionProvider cp : this.providers) {
            cp.close();
        }
        this.providers.clear();
        localProviders = new ThreadLocal<ConnectionProvider>();
    }

    /**
     * Closes the given connection
     * 
     * @param conn Connection
     * @throws SQLException ex
     */
    public void closeConnection(final Connection conn) throws SQLException {
        this.getCurrentProvider().closeConnection(conn);
    }

    /**
     * Configures the ConnectionProvider with the given properties
     * 
     * @param props Properties
     */
    public void configure(final Properties props) {
        this.properties = props;
    }

    /**
     * Gets a connection from the ConnectionProvider
     * 
     * @return Connection
     * @throws SQLException ex
     */
    public Connection getConnection() throws SQLException {
        return this.getCurrentProvider().getConnection();
    }

    /**
     * Returns wether agressive release is supported
     * 
     * @return boolean
     */
    public boolean supportsAggressiveRelease() {
        return this.getCurrentProvider().supportsAggressiveRelease();
    }

    /**
     * Returns the connection provider for this thread. If none is found, one is created.
     * 
     * @return ConnectionProvider
     */
    public ConnectionProvider getCurrentProvider() {
        ConnectionProvider cp = localProviders.get();
        if (cp == null) {
            cp = new DriverManagerConnectionProvider();
            Properties p = new Properties(this.properties);
            String url = p.getProperty(Environment.URL);
            if (url != null) {
                url = url.replace("#id", Integer.toHexString(Thread.currentThread().hashCode()));
                p.setProperty(Environment.URL, url);
            }
            cp.configure(p);
            localProviders.set(cp);
            this.providers.add(cp);
        }
        LOGGER.info("Thread [" + Thread.currentThread().hashCode() + "] using Connection Provider [" + cp.hashCode()
                + "]");
        return cp;
    }

}
